/*
* Copyright (c) 2012 Kyle Hayward
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once

#include "Vector.h"

namespace OptixDotNet 
{
	/// <summary>
	/// Used to pass data between the host and an OptiX program running on the device( GPU ).
    /// </summary>
	public ref class Variable : public OptixNode
	{
	public:

		/// <summary>
		/// Checks to make sure the object is valid and consistent
        /// </summary>
		virtual void Validate() override
		{
			
		}

		/// <summary>
		/// Destroys the Optix resource
        /// </summary>
		virtual void Destroy() override
		{
			
		}

		/// <summary>
		/// Gets int data on the variable
        /// </summary>
		int GetInt()
		{
			int x;
			CheckError( rtVariableGet1i( mVariable, &x ) );

			return x;
		}

		/// <summary>
		/// Gets int data on the variable
        /// </summary>
		unsigned int GetUInt()
		{
			unsigned int x;
			CheckError( rtVariableGet1ui( mVariable, &x ) );
			return x;
		}

		/// <summary>
		/// Gets float data on the variable
        /// </summary>
		float GetFloat()
		{
			float x;
			CheckError( rtVariableGet1f( mVariable, &x ) );
			return x;
		}

		/// <summary>
		/// Gets int2 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		T GetInt2()
		{
			T var;
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableGet2iv( mVariable, (int*)ptr ) );

			return var;
		}

		/// <summary>
		/// Gets uint2 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		T GetUInt2()
		{
			T var;
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableGet2uiv( mVariable, (unsigned int*)ptr ) );

			return var;
		}

		/// <summary>
		/// Gets float2 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		T GetFloat2()
		{
			T var;
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableGet2fv( mVariable, (float*)ptr ) );

			return var;
		}

		/// <summary>
		/// Gets int3 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		T GetInt3()
		{
			T var;
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableGet3iv( mVariable, (int*)ptr ) );

			return var;
		}

		/// <summary>
		/// Gets uint3 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		T GetUInt3()
		{
			T var;
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableGet3uiv( mVariable, (unsigned int*)ptr ) );

			return var;
		}

		/// <summary>
		/// Gets float3 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		T GetFloat3T()
		{
			T var;
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableGet3fv( mVariable, (float*)ptr ) );

			return var;
		}

		/// <summary>
		/// Gets int4 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		T GetInt4()
		{
			T var;
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableGet4iv( mVariable, (int*)ptr ) );

			return var;
		}

		/// <summary>
		/// Gets uint4 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		T GetUInt4()
		{
			T var;
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableGet4uiv( mVariable, (unsigned int*)ptr ) );

			return var;
		}

		/// <summary>
		/// Gets float4 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		T GetFloat4()
		{
			T var;
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableGet4fv( mVariable, (float*)ptr ) );

			return var;
		}

		/// <summary>
		/// Sets int data on the variable
        /// </summary>
		void Set( int x )
		{
			CheckError( rtVariableSet1i( mVariable, x ) );
		}

		/// <summary>
		/// Sets int data on the variable
        /// </summary>
		void Set( unsigned int x )
		{
			CheckError( rtVariableSet1ui( mVariable, x ) );
		}

		/// <summary>
		/// Sets float2 data on the variable
        /// </summary>
		void Set( float x )
		{
			CheckError( rtVariableSet1f( mVariable, x ) );
		}

		/// <summary>
		/// Sets int2 data on the variable
        /// </summary>
		void Set( int x, int y )
		{
			CheckError( rtVariableSet2i( mVariable, x, y ) );
		}

		/// <summary>
		/// Sets int2 data on the variable
        /// </summary>
		void Set( OptixDotNet::Math::Int2% var )
		{
			pin_ptr<OptixDotNet::Math::Int2> ptr = &var;
			CheckError( rtVariableSet2iv( mVariable, (int*)ptr ) );
		}

		/// <summary>
		/// Sets int2 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		void SetInt2( T var )
		{
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableSet2iv( mVariable, (int*)ptr ) );
		}

		/// <summary>
		/// Sets uint2 data on the variable
        /// </summary>
		void Set( unsigned int x, unsigned int y )
		{
			CheckError( rtVariableSet2ui( mVariable, x, y ) );
		}

		/// <summary>
		/// Sets uint2 data on the variable
        /// </summary>
		void Set( OptixDotNet::Math::UInt2% var )
		{
			pin_ptr<OptixDotNet::Math::UInt2> ptr = &var;
			CheckError( rtVariableSet2uiv( mVariable, (unsigned int*)ptr ) );
		}

		/// <summary>
		/// Sets uint2 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		void SetUInt2( T var )
		{
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableSet2uiv( mVariable, (unsigned int*)ptr ) );
		}

		/// <summary>
		/// Sets float2 data on the variable
        /// </summary>
		void Set( float x, float y )
		{
			CheckError( rtVariableSet2f( mVariable, x, y ) );
		}

		/// <summary>
		/// Sets float2 data on the variable
        /// </summary>
		void Set( OptixDotNet::Math::Vector2% var )
		{
			pin_ptr<OptixDotNet::Math::Vector2> ptr = &var;
			CheckError( rtVariableSet2fv( mVariable, (float*)ptr ) );
		}

		/// <summary>
		/// Sets int3 data on the variable
        /// </summary>
		void Set( int x, int y, int z )
		{
			CheckError( rtVariableSet3i( mVariable, x, y, z ) );
		}

		/// <summary>
		/// Sets int3 data on the variable
        /// </summary>
		void Set( OptixDotNet::Math::Int3% var )
		{
			pin_ptr<OptixDotNet::Math::Int3> ptr = &var;
			CheckError( rtVariableSet3iv( mVariable, (int*)ptr ) );
		}

		/// <summary>
		/// Sets int3 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		void SetInt3( T var )
		{
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableSet3iv( mVariable, (int*)ptr ) );
		}

		/// <summary>
		/// Sets uint3 data on the variable
        /// </summary>
		void Set( unsigned int x, unsigned int y, unsigned int z )
		{
			CheckError( rtVariableSet3ui( mVariable, x, y, z ) );
		}

		/// <summary>
		/// Sets uint3 data on the variable
        /// </summary>
		void Set( OptixDotNet::Math::UInt3% var )
		{
			pin_ptr<OptixDotNet::Math::UInt3> ptr = &var;
			CheckError( rtVariableSet3uiv( mVariable, (unsigned int*)ptr ) );
		}

		/// <summary>
		/// Sets uint3 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		void SetUInt3( T var )
		{
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableSet3uiv( mVariable, (unsigned int*)ptr ) );
		}

		/// <summary>
		/// Sets float3 data on the variable
        /// </summary>
		void Set( float x, float y, float z )
		{
			CheckError( rtVariableSet3f( mVariable, x, y, z ) );
		}

		/// <summary>
		/// Sets float3 data on the variable
        /// </summary>
		void Set( OptixDotNet::Math::Vector3% var )
		{
			pin_ptr<OptixDotNet::Math::Vector3> ptr = &var;
			CheckError( rtVariableSet3fv( mVariable, (float*)ptr ) );
		}

		/// <summary>
		/// Sets float3 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		void SetFloat3( T var )
		{
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableSet3fv( mVariable, (float*)ptr ) );
		}

		/// <summary>
		/// Sets int4 data on the variable
        /// </summary>
		void Set( int x, int y, int z, int w )
		{
			CheckError( rtVariableSet4i( mVariable, x, y, z, w ) );
		}

		/// <summary>
		/// Sets int4 data on the variable
        /// </summary>
		void Set( OptixDotNet::Math::Int4% var )
		{
			pin_ptr<OptixDotNet::Math::Int4> ptr = &var;
			CheckError( rtVariableSet4iv( mVariable, (int*)ptr ) );
		}

		/// <summary>
		/// Sets int4 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		void SetInt4( T var )
		{
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableSet4iv( mVariable, (int*)ptr ) );
		}

		/// <summary>
		/// Sets uint4 data on the variable
        /// </summary>
		void Set( unsigned int x, unsigned int y, unsigned int z, unsigned int w )
		{
			CheckError( rtVariableSet4ui( mVariable, x, y, z, w ) );
		}

		/// <summary>
		/// Sets uint4 data on the variable
        /// </summary>
		void Set( OptixDotNet::Math::UInt4% var )
		{
			pin_ptr<OptixDotNet::Math::UInt4> ptr = &var;
			CheckError( rtVariableSet4uiv( mVariable, (unsigned int*)ptr ) );
		}

		/// <summary>
		/// Sets uint4 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		void SetUInt4( T var )
		{
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableSet4uiv( mVariable, (unsigned int*)ptr ) );
		}

		/// <summary>
		/// Sets float4 data on the variable
        /// </summary>
		void Set( float x, float y, float z, float w )
		{
			CheckError( rtVariableSet4f( mVariable, x, y, z, w ) );
		}	

		/// <summary>
		/// Sets float4 data on the variable
        /// </summary>
		void Set( OptixDotNet::Math::Vector4% var )
		{
			pin_ptr<OptixDotNet::Math::Vector4> ptr = &var;
			CheckError( rtVariableSet4fv( mVariable, (float*)ptr ) );
		}

		/// <summary>
		/// Sets float4 data on the variable
        /// </summary>
		generic< typename T > where T : value class
		void SetFloat4( T var )
		{
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableSet4fv( mVariable, (float*)ptr ) );
		}

		/// <summary>
		/// Sets an Optix data object on the variable.
		/// The object can be: Buffer, GeometryGroup, Group, Selector, Transform, or TextureSampler
        /// </summary>
		void Set( DataNode^ object )
		{
			CheckError( rtVariableSetObject( mVariable, object->ObjectPtr() ) );
		}

		/// <summary>
		/// Gets the user data set on the object
        /// </summary>
		generic< typename T > where T : value class
		T GetUserData()
		{
			T var;
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableGetUserData( mVariable, sizeof( T ), ptr ) );

			return var;
		}

		/// <summary>
		/// Sets the user data on the object
        /// </summary>
		generic< typename T > where T : value class
		void SetUserData( T var )
		{
			pin_ptr<T> ptr = &var;
			CheckError( rtVariableSetUserData( mVariable, sizeof( T ), ptr ) );
		}

		/// <summary>
		/// Gets the annotation set on the variable in the cuda program
        /// </summary>
		property System::String^ Annotation
		{
			System::String^ get()
			{
				const char* str = 0;
				CheckError( rtVariableGetAnnotation( mVariable, &str ) );

				return AnsiiToStr( str );
			}
		}

		/// <summary>
		/// Gets the type of object set on the variable
        /// </summary>
		property OptixDotNet::Type Type
		{
			OptixDotNet::Type get()
			{
				RTobjecttype type;
				CheckError( rtVariableGetType( mVariable, &type ) );

				return (OptixDotNet::Type)type;
			}
		}

	internal:
		Variable( Context^ context, RTvariable var );
		inline RTvariable InternalPtr() { return mVariable; }

	private:

		RTvariable	mVariable;
	};
}